package com.lyb.hardware.cpu;

import com.lyb.Utils;
import com.lyb.constant.StatusConstant;
import com.lyb.constant.SystemConstant;
import com.lyb.hardware.AlpacaPCB;
import com.lyb.statement.ExecutableStatement;

/**
 * CPU类，负责执行可执行文件
 *
 * @author Mr.Alpaca potato
 * @version 1.4.0
 */
public class AlpacaCpu implements Runnable {
    /**
     * 核心寄存器组
     * PSW、DR、IR、PC
     */
    private AlpacaRegister register;
    /**
     * 当前正在执行的进程
     */
    private AlpacaPCB currentPcb;
    /**
     * 当前进程时间片、进程相对时间(秒)
     */
    private static int timeSlice;
    /**
     * 系统开机时间(毫秒)
     */
    private static final long SYSTEM_TIME = SystemConstant.SYSTEM_START_TIME;


    public AlpacaCpu() {
        /* 初始化寄存器 */
        register = new AlpacaRegister();
    }

    /**
     * CPU执行函数
     */
    public void cpu() {
        /* 若无就绪进程则执行闲逛进程 */
        if (SystemConstant.MAIN_BOARD.getReadyQueue().isEmpty()) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.exit(100);
            }
            return;
        }

        /* 1.获取一个执行进程 */
        currentPcb = SystemConstant.MAIN_BOARD.getReadyQueue().poll();
        /* 2.恢复现场 */
        register = currentPcb.load();
        /* 模拟执行指定时间片(命令数量)，循环结束视为该进程时间片结束 */
        for (int i = 0; i < SystemConstant.PROCESS_TIME; i++) {
            timeSlice = SystemConstant.PROCESS_TIME - i;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                System.exit(100);
            }
            /* 3.判断PSW状态 */
            String psw = register.getPsw();
            /* 若进程处于可执行状态继续执行 */
            if (psw.equals(StatusConstant.NORMAL)) {
                /* 4.获取指令并使PC指向下一条语句 */
                next();
                /* 5.执行指令 */
                execute();
            } else if (psw.equals(StatusConstant.PROCESS_END)) { /* 进程结束 */
                SystemConstant.MAIN_BOARD.destroy();
                return;
            } else if (psw.equals(StatusConstant.IO_BLOCK)) { /* 进程阻塞 */
                SystemConstant.MAIN_BOARD.block();
                return;
            }
        }

        /* 6.若时间片结束时进程也刚好结束则撤销进程 */
        if (register.getPsw().equals(StatusConstant.PROCESS_END)) {
            SystemConstant.MAIN_BOARD.destroy();
            return;
        }

        /* 7.进程时间片结束，保存现场，并将进程放回就绪队列中 */
        currentPcb.save(register);
        SystemConstant.MAIN_BOARD.getReadyQueue().add(currentPcb);
    }

    /**
     * 获取指令并使PC指向下一条指令索引
     * 1.取出当前PC内存的指令
     * 2.将取出指令存入指令寄存器
     * 3.PC指向下一条指令
     */
    private void next() {
        /* 从内存中取出当前PC所指向的内存中的指令 */
        String statement = SystemConstant.MAIN_BOARD.readContentFromMemory(register.getPc());
        /* 若指令未填满每一行则将用于填充的空子节剔除 */
        int lastIndex = statement.indexOf("\u0000");
        if (lastIndex != -1) {
            statement = statement.substring(0, lastIndex);
        }

        /* 将取出的指令存入指令寄存器 */
        register.setIr(statement);
        /* PC自增 */
        register.setPc(register.getPc() + 1);

        /* 若PC已指向进程文件最后一行则表明程序运行结束 */
        if (register.getPc() > currentPcb.getEndIndex()) {
            register.setPsw(StatusConstant.PROCESS_END);
        }
    }


    /* ********** 执行指令方法 ********** */

    /**
     * 解释并执行指令寄存器中的指令
     */
    private void execute() {
        /* 若PC已指向进程文件最后一行则表明程序运行结束 */
        if (register.getPc() > currentPcb.getEndIndex() + 1) {
            return;
        }

        /* 获取指令 */
        String statement = register.getIr();

        /* 行数+1 */
        currentPcb.setRow(currentPcb.getRow() + 1);

        if (SystemConstant.END.equals(statement)) {
            /* 修改PSW为结束状态 */
            register.setPsw(StatusConstant.PROCESS_END);
            return;
        }

        /* 若当前行为空行则直接跳过 */
        if ("".equals(statement)) {
            return;
        }

        /* 寻找可执行该语句的服务类，若不为null则调用服务类的执行方法 */
        ExecutableStatement execution = findExecution(currentPcb.getRow(), statement);
        if (execution != null) {
            /* 执行语句 */
            execution.execute(statement, register.getDr());
        } else {
            Utils.log("[系统|编译器]: 第 " + currentPcb.getRow() + " 行: \""+ statement + "\" 无法执行", SystemConstant.SYSTEM_LOG_PATH);
        }
    }

    /**
     * 从可执行语句服务类列表中依次获得各服务类负责的可执行语句，与当前语句一一比较
     * 若寻找到可能执行该语句的服务类则调用其格式判断
     * 符合则返回该服务类，不符合则返回null
     *
     * @param statement 输入语句
     * @return 可执行该语句的服务类 null表示没有对应服务类
     */
    private ExecutableStatement findExecution(int row, String statement) {
        /* 通过匹配关键字，寻找可能执行该语句的服务类 */
        for (ExecutableStatement execution : SystemConstant.EXECUTABLE_STATEMENT_LIST) {
            if (statement.contains(execution.getStatement())) {
                /* 若输入语句也符合该服务类的处理格式则直接返回该服务类 */
                if (execution.formatJudge(row, statement)) {
                    return execution;
                }
            }
        }
        return null;
    }


    /* ********** Getter & Setter ********** */

    public AlpacaRegister getRegister() {
        return register;
    }

    public AlpacaPCB getCurrentPcb() {
        return currentPcb;
    }

    public int getTimeSlice() { return timeSlice; }


    /**
     * CPU在一个单独的线程执行方法
     */
    @Override
    @SuppressWarnings("all")
    public void run() {
        while (true) {
            cpu();
        }
    }
}
